iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 7
1

當有些資料是想要從網絡上獲取到app顯示時
就須要串接資料所提供的api
可以利用retrofit這個工具進行

Step 1: 加入Retrofit dependencies to Gradle

  1. Open build.gradle (Module: app).
  2. In the dependencies section, add these lines for the Retrofit libraries:
implementation "com.squareup.retrofit2:retrofit:2.3.0"
implementation "com.squareup.retrofit2:converter-scalars:2.3.0"

Step 2: data class(JSON)

Retrofit根據來自Web服務的內容為應用程序創建網絡API,支援的Web內容包含XML與JSON格式

這裡我是用高雄市政府公開的臨時停車資訊做練習,可查詢其公開的網頁點選JSON,通常會是類似這樣的網址https://api.kcg.gov.tw/api/service/get/897e552a-2887-4f6f-a6ee-709f7fbe0ee3
內容可能是密密麻麻一大串文字
因爲我們要將接進來的資訊程式化,所以會做成data class
可以利用例如https://jsoneditoronline.org/ 之類的網站協助解析

不過,android studio也有插件可以協助

到setting選Plugins,收尋JsonToKotlinClass,然後安裝

新增一個ParkingInfo.kt,右鍵開啓選單選Generate,再選Kotlin classes from JSON開啓視窗,這裡可以將整個Json文字,或只需要該網址貼上,解析完後新增

就會協助我們產生data class

data class ParkingInfo(
    @SerializedName("data")
    val `data`: List<Data> = listOf(),
    @SerializedName("id")
    val id: String = "", // 914766c5-8eda-42df-ac8b-f207b6fb0dd5
    @SerializedName("isImage")
    val isImage: Boolean = false, // false
    @SerializedName("success")
    val success: Boolean = false // true
) {
    data class Data(
        @SerializedName("seq")
        val seq: Int = 0, // 1
        @SerializedName("可提供小型車停車位")
        val 可提供小型車停車位: String = "", // 10
        @SerializedName("地址")
        val 地址: String = "", // 金龍路65號
        @SerializedName("臨時停車處所")
        val 臨時停車處所: String = "", // 老人文康活動中心
        @SerializedName("行政區")
        val 行政區: String = "" // 大社區
    )
}

不過因爲對方提供的內容關係,有可能會看到例如val 可提供小型車停車位: String = ""以中文爲變數名稱
這時就可利用@SerializedName將對方中文名稱傳入,我們的變數另外命名,這樣就可對應

@SerializedName("可提供小型車停車位")
val carSpace: String

data class name也可修改爲自己要的,調整後如下

data class ParkingInfo(
    @SerializedName("isImage")
    val isImage: Boolean,

    @SerializedName("data")
    val `data`: List<ParkingDetail>,

    @SerializedName("id")
    val id: String,

    @SerializedName("success")
    val isSuccess: Boolean

) {
    data class ParkingDetail(
        @SerializedName("行政區")
        val gov: String,

        @SerializedName("臨時停車處所")
        val parkingPlace: String,

        @SerializedName("可提供小型車停車位")
        val carSpace: String,

        @SerializedName("地址")
        val address: String,

        @SerializedName("seq")
        val seq: Int
    )
}

完成後就來設定API

Step 3: 實作Api Service

新增一個ApiService.kt檔案,並宣告BASE_URL爲const val,(kotlin)

要注意BASE_URL內的網址須爲斜槓/結尾

private const val BASE_URL = 
   "https://api.kcg.gov.tw/api/service/get/"

define一個interface用於向web server做http請求
(須要import retrofit2.http.GET , retrofit2.call)

interface ParkingApiService {
    @GET("897e552a-2887-4f6f-a6ee-709f7fbe0ee3")
    fun getParkingLot(): Call<ParkingInfo>
}   

本次任務是從Web獲取資料,所以interface內只需定義一個回傳資料的getParkingLot()方法
getParkingLot()並會建立一個Call object用於發起請求
而在此方法前的@GET,是將欲請求的網址路徑或endpoint傳入
如果要用網址的話,就可設定爲https://api.kcg.gov.tw/api/service/get/897e552a-2887-4f6f-a6ee-709f7fbe0ee3
用於endpoint的話,retrofit就會將此endpoint接在BASE_URL後

以retrofit builder建立一個retrofit object

object ParkingApi{
    private val retrofit = Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl(BASE_URL)
        .build()
    val retrofitService: ParkingApiService = retrofit.create(ParkingApiService::class.java)
}

以retrofit建立web service api至少包含二個方法:baseUrl(),addConverterFactory()
轉換器converter告訴retrofit如何處理從web取回的資料
GsonConverterFactory會幫我們將拿到的Json,轉換爲data class所設定的格式,最後呼叫build()建立

接着以create()將interface ParkingApiService實作出來
因爲建立api是比較耗能的,而且app也只需要一個retrofit實例
所以將其宣告爲object單例

Step 4: 開啓app網路權限

向Web發出請求需要app告訴android系統開啓使用網路的權限
app/manifests/AndroidManifest.xml
新增<uses-permission android:name="android.permission.INTERNET" />
位置如下

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.carparking">

    <uses-permission android:name="android.permission.INTERNET" />
    
    <application ...
    ..
    .

Step 5: 呼叫Api Service

到MainActivity中,準備呼叫api

ParkingApi.retrofitService.getParkingLot()
            .enqueue(object : retrofit2.Callback<ParkingInfo> {})                

使用ParkingApi.retrofitService.getParkingLot()回傳的Call object呼叫enqueue() ,將向網路發起request請求排入後台執行緒

object會顯示錯誤提示,因爲此處有二個fun必須複寫
分別是onResponse()onFailure(),按ctrl+o

onFailure()就是當發出的request沒有獲得正確回應時,要做的事
我們先寫log,如果fail,就讓它丟個訊息

override fun onFailure(call: Call<ParkingInfo>, t: Throwable) {
                    Log.e("Failure", t.message)
                }

onResponse()是我們主要看的,因爲要成功接進來才能拿到資料

override fun onResponse(call: Call<ParkingInfo>, response: Response<ParkingInfo>) {
                    if (response.isSuccessful) {                        
                        response.body()?.data?.forEach {
                            Log.d("api", it.toString())
                    }

執行app後,從logcat可以看到有成功的將資料取得

參考
https://codelabs.developers.google.com/codelabs/kotlin-android-training-internet-data/


上一篇
Day 6--Room(二) 實作,爲app建立一個資料庫,新增資料存入
下一篇
Day 8--RecyclerView(一)簡介
系列文
程式初學:Android與Kotlin30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言